﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Text.RegularExpressions;
using System.Net;
using System.Net.Security;

namespace WoWAPI
{
	internal class CustomWebRequest
	{
		public Uri RequestUri { get; set; }
		public Dictionary<string, string> Headers { get;set; }

		private CustomWebRequest()
		{
			this.Headers = new Dictionary<string,string>();
		}

		public CustomWebRequest(string Url)
			: this()
		{
			RequestUri = new Uri(Url);
		}

		public CustomWebRequest(Uri Uri)
			: this()
		{
			RequestUri = Uri;			
		}

		public string GetResponse()
		{
			//set up headers
			if (!this.Headers.ContainsKey("Host"))
				this.Headers.Add("Host", RequestUri.Host);
			if (!this.Headers.ContainsKey("Date"))
				this.Headers.Add("Date", DateTime.Now.ToString("R"));
			if (!this.Headers.ContainsKey("Connection"))
				this.Headers.Add("Connection", "Keep-Alive");

			string responseBody = String.Empty;
			using (System.Net.Sockets.TcpClient client = new System.Net.Sockets.TcpClient(RequestUri.Host, RequestUri.Port))
			{

				//build request
				string requestMethod = "GET " + RequestUri.PathAndQuery + " HTTP/1.0\r\n";

				StringBuilder requestHeaders = new StringBuilder();
				foreach (KeyValuePair<string, string> header in this.Headers)
				{
					requestHeaders.AppendFormat("{0}: {1}\r\n", header.Key, header.Value);
				}
				requestHeaders.Append("\r\n");
				string body = "";

				Byte[] data = System.Text.Encoding.ASCII.GetBytes(requestMethod + requestHeaders.ToString() + body);

				string responseData = String.Empty;
				// Send the message to the connected TcpServer.
				if (RequestUri.Scheme == "https")
				{
					SslStream stream = new SslStream(client.GetStream());

					stream.AuthenticateAsClient(RequestUri.Host);

					stream.Write(data, 0, data.Length);
					stream.Flush();

					// Receive the TcpServer.response.
					byte[] bytes = new byte[1024];
					System.Text.StringBuilder sb = new System.Text.StringBuilder();
					int count = 0;
					do
					{
						count = stream.Read(bytes, 0, 1024);
						if (count == 0)
						{
							break;
						}
						sb.Append(System.Text.Encoding.UTF8.GetString(bytes, 0, count));
					} while (count > 0);

					responseData = sb.ToString();
				}
				else
				{
					NetworkStream stream = client.GetStream();
					stream.Write(data, 0, data.Length);
					stream.Flush();

					// Receive the TcpServer.response.
					byte[] bytes = new byte[1024];
					System.Text.StringBuilder sb = new System.Text.StringBuilder();
					int count = 0;
					do 
					{
						count = stream.Read(bytes, 0, 1024);
						if (count == 0)
						{
							break;
						}
						sb.Append(System.Text.Encoding.UTF8.GetString(bytes, 0, count));
					} while (count > 0);

					responseData = sb.ToString();
				}
				// Close everything.
				client.Close();

				responseBody = Regex.Replace(responseData, @"^.*?Content-Type: [^\r\n]+[\r\n]+(.*)", "$1", RegexOptions.IgnoreCase | RegexOptions.Singleline);
				System.Text.UTF8Encoding encoding = new UTF8Encoding();

				//process response codes			
				Match responseCodeMatch = Regex.Match(responseData, @"^HTTP/1.1\s([\d\.]+)\s(.*?)[\r\n]", RegexOptions.IgnoreCase);
				HttpStatusCode responseStatus = (HttpStatusCode)Convert.ToInt32(responseCodeMatch.Groups[1].Value);
				if (responseStatus != HttpStatusCode.OK)
				{
					string responseMessage = responseCodeMatch.Groups[2].Value;
					CustomWebException ex = new CustomWebException(responseMessage);
					ex.ResponseBody = responseBody;
					ex.ResponseStatus = responseStatus;
					throw ex;
				}
			}



			return responseBody;
		}
	}

	internal class CustomWebException : System.Exception
	{
		public HttpStatusCode ResponseStatus { get; set; }
		public string ResponseBody { get; set; }

		public CustomWebException(string message) : base(message) { }
	}
}
